#include "FileSystem.hpp"
#include "ReadFile.hpp"
#include "WriteFile.hpp"
#include "Logger.hpp"
#include <Windows.h>

namespace ge {

//----------------------------------------------------------------------------//
// FileSystem
//----------------------------------------------------------------------------//

//----------------------------------------------------------------------------//
FileSystem::FileSystem( void )
{
     char _buff[1024];
     uint _length = GetModuleFileNameA( 0, _buff, 1023 );
     for( int i = _length; i > 0; -- i )
     {
          if( _buff[ i ] == '/' || _buff[ i ] == '\\' )
          {
                    m_app_name = ( _buff + i + 1 );
                    _buff[ i ] = 0;
                    m_app_dir = MakeFullPath( _buff, "" ) + "/";
                    break;
          }
     }
     
     m_resources_dir = m_app_dir;

     m_logger = new Logger();
}
//----------------------------------------------------------------------------//
FileSystem::~FileSystem( void )
{
}
//----------------------------------------------------------------------------//
bool FileSystem::IsFullPath( const String& _path )
{      
     return _path[1] == ':'; // X:/
}
//----------------------------------------------------------------------------//
String FileSystem::Device( const String& _path )
{
     return geChoose( IsFullPath( _path ), _path.SubString( 0, 1 ), 0 );
}
//----------------------------------------------------------------------------//
String FileSystem::PathTo( const String& _path )
{
     uint c = _path.Length();
     for( ; c > 0 && ( _path[c-1] == '/' || _path[c-1] == '\\' ); -- c );
     for( ; c > 0 && _path[c-1] != '/' && _path[c-1] != '\\'; -- c );
     return _path.SubString( 0, c ); 
}
//----------------------------------------------------------------------------//
String FileSystem::Name( const String& _path )
{
     uint c = _path.Length();
     for( ; c > 0 && _path[c-1] != '/' && _path[c-1] != '\\'; -- c );
     return _path.SubString( c ); 
}
//----------------------------------------------------------------------------//
String FileSystem::ShortName( const String& _path )
{
     String _name = Name( _path );
     uint c = _name.Length();
     for( ; c > 0 && _name[c] != '.'; -- c );
     return geChoose( c > 0, _name.SubString( 0, c ), _name ); 
}
//----------------------------------------------------------------------------//
String FileSystem::Ext( const String& _path )
{
     String _ext = Name( _path );
     uint c = _ext.Length();
     for( ; c > 0 && _ext[c-1] != '.'; -- c );
     return geChoose( c > 0, _ext.SubString( c ), "" ); 
}
//----------------------------------------------------------------------------//
void FileSystem::SplitPath( const String& _path, String* _device, StringArray* _items )
{
     uint c = 0, p = 0;

     for( ; c < _path.Length() && _path[c] != ':'; ++ c );
     if( c < _path.Length() )
     {
          if( _device ) *_device = _path.SubString( 0, c );
          p = ++ c;
     }
     else c = 0;
     
     if( _items )
     {
          StringArray _tmp;
          for( ; c < _path.Length(); ++ c )
          {
               if( _path[c] == '/' || _path[c] == '\\' )
               {
                    if( c != p ) _tmp.Append( _path.SubString( p, c - p ) );
                    p = c + 1;
               }
          }
          if( c != p ) _tmp.Append( _path.SubString( p, c - p ) );
          
          _items -> Clear();
          for( uint i = 0; i < _tmp.Size(); ++ i )
          {
               if( _tmp[i] == ".." )
               {
                    if( _items -> IsEmpty() ) _items -> Append( ".." );
                    else _items -> RemoveLast();
               }
               else if( _tmp[i] != "." ) _items -> Append( _tmp[i] );
          }
     }
}
//----------------------------------------------------------------------------//
String FileSystem::MakePath( const String& _device, const StringArray& _items )
{
     String _path;
     if( !_device.IsEmpty() )  _path += _device + ":/";
     if( _items.IsEmpty() ) _path += geChoose( _device.IsEmpty(), "./", "/" );
     for( uint i = 0; i < _items.Count(); ++ i ) 
     { 
          if( i == 0 ) _path += _items[i];
          else _path /= _items[i];
     }
     return _path;
}
//----------------------------------------------------------------------------//
String FileSystem::MakeName( const String& _name, const String& _ext )
{
     String _fname;
     if( _name.NonEmpty() )
     {
          _fname += _name;
          if( _ext.NonEmpty() ) _fname += '.' + _ext; 
     }
     return _fname;
}
//----------------------------------------------------------------------------//
String FileSystem::MakeFullPath( const String& _path, const String& _base )
{
     String _d, _pd;
     StringArray _p, _b, _f;

     SplitPath( _path, &_pd, &_p );
     if( IsFullPath( _path ) ) return MakePath( _pd, _p );

     SplitPath( _base, &_d, &_b );

     _f = _b;

     for( uint i = 0; i < _p.Count(); ++ i )
     {
          if( _p[i] == ".." )
          {
               if( _f.IsEmpty() ) _f.Append( ".." );
               _f.RemoveLast();
          }
          else if( _p[i] != "." ) _f.Append( _p[i] );
     } 

     return MakePath( _d, _f );
}
//----------------------------------------------------------------------------//
String FileSystem::ToFullPath( const String& _path )
{
     return MakeFullPath( _path, Instance().m_app_dir );
}
//----------------------------------------------------------------------------//
bool FileSystem::_IsObjectExists( const String& _full_path )
{
     assert( IsFullPath( _full_path ) );

     return GetFileAttributesA( _full_path ) != 0xffffffff;
}
//----------------------------------------------------------------------------//
bool FileSystem::_IsFile( const String& _full_path )
{
     assert( IsFullPath( _full_path ) );

     return ( GetFileAttributesA( _full_path ) & FILE_ATTRIBUTE_DIRECTORY ) == 0;
}
//----------------------------------------------------------------------------//
bool FileSystem::_CreateDir( const String& _full_path )
{
     assert( IsFullPath( _full_path ) );

     String _d;
     StringArray _p;
     SplitPath( _full_path, &_d, &_p );
     String _cpath = _d + ":/";

     if( !_IsObjectExists( _cpath ) ) return false; // device not exists

     for( uint i = 0; i < _p.Count(); ++ i )
     {
          _cpath /= _p[i];

          if( !_IsObjectExists( _cpath ) )
          {
               if( !CreateDirectoryA( _cpath.Data(), 0 ) ) return false; // could not create directory
          } 
          else if( _IsFile( _cpath ) ) return false; // object is file
     }

     return true;
}
//----------------------------------------------------------------------------//
bool FileSystem::FileExists( const String& _path )
{
     String _fpath = ToFullPath( _path );
     return _IsObjectExists( _fpath ) && _IsFile( _fpath ); 
}
//----------------------------------------------------------------------------//
bool FileSystem::DirectoryExists( const String& _path )
{
     String _fpath = ToFullPath( _path );
     return _IsObjectExists( _fpath ) && !_IsFile( _fpath ); 
}
//----------------------------------------------------------------------------//
InputStreamPtr FileSystem::OpenFile( const String& _path, bool _throw )
{
     String _fpath = ToFullPath( _path );
     if( _IsObjectExists( _fpath ) && _IsFile( _fpath ) )
     {
          try
          {
               ReadFilePtr _file = new ReadFile();
               _file -> _Open( _fpath );
               return _file.Get();
          }
          catch( ... )
          {
               if( _throw ) throw;
          }
     }
     else if( _throw ) throw geExceptionT( IOError, "FileSystem - File \"%s\" was not found", _fpath.Data() );
     return 0;
}
//----------------------------------------------------------------------------//
OutputStreamPtr FileSystem::RewriteFile( const String& _path, bool _throw )
{
     String _fpath = ToFullPath( _path );
     if( _CreateDir( PathTo( _fpath ) ) )
     {
          try
          {
               WriteFilePtr _file = new WriteFile();
               _file -> _Rewrite( _fpath );
               return _file.Get();
          }
          catch( ... )
          {
               if( _throw ) throw;
          }
     }
     else if( _throw ) throw geExceptionT( IOError, "FileSystem: Could not create directory \"%s\"", PathTo( _fpath ).Data() ); 
     return 0;
}
//----------------------------------------------------------------------------//

//----------------------------------------------------------------------------//
//
//----------------------------------------------------------------------------//

} // namespace ge
